package com.starsky.common.utils.file;

import com.starsky.common.utils.DateUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.*;

/**
 * zip文件下载
 */
public class ZipHelper {
    private static Logger log = LoggerFactory.getLogger(ZipHelper.class);
    private static final int BUFFER_SIZE = 1024;
    private static final String PATH_SEP = "/";

    /**
     * @param sourceFileName：输入目录或者文件
     * @param destFileName：生成zip压缩文件
     * @throws IOException
     */
    public static void zipCompress(String sourceFileName, String destFileName) throws IOException {
        File sourceFile = new File(sourceFileName);
        File destFile = new File(destFileName);
        zipCompress(sourceFile, destFile);
    }

    /**
     * @param sourceFile：输入目录或者文件
     * @param destFile：生成zip压缩文件
     * @throws IOException
     */
    public static void zipCompress(File sourceFile, File destFile) throws IOException {
        FileOutputStream fos = null;
        CheckedOutputStream cos = null;
        ZipOutputStream zos = null;
        try {
            fos = new FileOutputStream(destFile);
            cos = new CheckedOutputStream(fos, new CRC32());
            zos = new ZipOutputStream(cos);
            zipCompress(sourceFile, zos, "");
            zos.flush();
            zos.finish();
        } finally {
            if (zos != null)
                zos.close();
            if (cos != null)
                cos.close();
            if (fos != null)
                fos.close();
        }
    }

    /**
     * @param sourceFileName: 输入文件
     * @param zos：输出文件流
     * @throws IOException
     */
    private static void zipCompress(String sourceFileName, OutputStream zos) throws IOException {
        File sourceFile = new File(sourceFileName);
        if (sourceFile.isDirectory()) {
            zipCompressDir(sourceFile, new ZipOutputStream(zos), "");
        } else {
            zipCompressFile(sourceFile, new ZipOutputStream(zos), "");
        }
    }

    /**
     * @param sourceFile
     * @param zos
     * @param basePath
     * @throws IOException
     */
    private static void zipCompress(File sourceFile, ZipOutputStream zos, String basePath) throws IOException {
        if (sourceFile.isDirectory()) {
            zipCompressDir(sourceFile, zos, basePath);
        } else {
            zipCompressFile(sourceFile, zos, basePath);
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param dir      文件名
     * @param zos      输出流
     * @param basePath 文件路径
     * @throws IOException
     */
    private static void zipCompressDir(File dir, ZipOutputStream zos, String basePath) throws IOException {
        File[] files = dir.listFiles();
        String newBasePath = basePath + dir.getName() + PATH_SEP;
        if (files.length <= 0) {
            ZipEntry entry = new ZipEntry(newBasePath);
            zos.putNextEntry(entry);
            zos.closeEntry();
        }
        for (File file : files) {
            zipCompress(file, zos, newBasePath);
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param file     文件名
     * @param zos      输出流
     * @param basePath 文件路径
     * @throws IOException
     */
    private static void zipCompressFile(File file, ZipOutputStream zos, String basePath) throws IOException {
        String entryName = basePath + file.getName();
        FileInputStream fis = new FileInputStream(file);
        zipCompress(fis, zos, entryName);
    }

    private static byte[] zipCompress(InputStream is, String entryName) throws IOException {
        ByteArrayOutputStream baos = null;
        CheckedOutputStream cos = null;
        ZipOutputStream zos = null;
        try {
            baos = new ByteArrayOutputStream();
            cos = new CheckedOutputStream(baos, new CRC32());
            zos = new ZipOutputStream(cos);

            zos.setMethod(ZipOutputStream.DEFLATED);
            zipCompress(is, zos, entryName);
            zos.flush();
            zos.finish();
            byte[] result = baos.toByteArray();
            return result;
        } finally {
            if (zos != null)
                zos.close();
            if (cos != null)
                cos.close();
            if (baos != null)
                baos.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param is        输入流
     * @param zos       输出流
     * @param entryName 文件名
     * @throws IOException
     */
    private static void zipCompress(InputStream is, ZipOutputStream zos, String entryName) throws IOException {
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(is);
            ZipEntry entry = new ZipEntry(entryName);
            zos.putNextEntry(entry);
            int count;
            byte[] buffer = new byte[BUFFER_SIZE];
            while (true) {
                count = bis.read(buffer, 0, BUFFER_SIZE);
                if (count < 0)
                    break;
                zos.write(buffer, 0, count);
            }
            zos.closeEntry();
        } finally {
            if (bis != null)
                bis.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param sourceFileName
     * @param destDir
     * @throws IOException
     */
    public static void zipDecompress(String sourceFileName, String destDir) throws IOException {
        zipDecompress(new File(sourceFileName), new File(destDir));
    }

    /**
     * 生成zip压缩文件
     *
     * @param sourceFile
     * @param destDir
     * @throws IOException
     */
    public static void zipDecompress(File sourceFile, File destDir) throws IOException {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(sourceFile);
            zipDecompress(fis, destDir);
        } finally {
            if (fis != null)
                fis.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param sourceBytes
     * @param destDir
     * @throws IOException
     */
    public static void zipDecompress(byte[] sourceBytes, File destDir) throws IOException {
        ByteArrayInputStream bais = null;
        try {
            bais = new ByteArrayInputStream(sourceBytes);
            zipDecompress(bais, destDir);
        } finally {
            if (bais != null)
                bais.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param is
     * @param destFile
     * @throws IOException
     */
    public static void zipDecompress(InputStream is, File destFile) throws IOException {
        CheckedInputStream cis = null;
        ZipInputStream zis = null;
        try {
            cis = new CheckedInputStream(is, new CRC32());
            zis = new ZipInputStream(cis);
            zipDecompress(zis, destFile);
        } finally {
            if (zis != null)
                zis.close();
            if (cis != null)
                cis.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param zis
     * @param destFile
     * @throws IOException
     */
    private static void zipDecompress(ZipInputStream zis, File destFile) throws IOException {
        while (true) {
            ZipEntry entry = zis.getNextEntry();
            if (entry == null)
                break;
            String dir = destFile.getPath() + File.separator + entry.getName();
            File dirFile = new File(dir);
            ensureFolderExists(dirFile);

            if (entry.isDirectory()) {
                dirFile.mkdir();
            } else {
                zipDecompressFile(zis, dirFile);
            }
            zis.closeEntry();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param zis
     * @param destFile
     * @throws IOException
     */
    private static void zipDecompressFile(ZipInputStream zis, File destFile) throws IOException {
        BufferedOutputStream bos = null;
        int count;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(destFile));
            byte[] buffer = new byte[BUFFER_SIZE];
            while (true) {
                count = zis.read(buffer, 0, BUFFER_SIZE);
                if (count < 0)
                    break;
                bos.write(buffer, 0, count);
            }
        } finally {
            if (bos != null)
                bos.close();
        }
    }

    /**
     * 生成zip压缩文件
     *
     * @param sourceFileName
     * @param destFileName
     * @throws IOException
     */
    public static void zipDecompressAsSingleFile(String sourceFileName, String destFileName) throws IOException {
        CheckedInputStream cis = null;
        ZipInputStream zis = null;
        try {
            cis = new CheckedInputStream(new FileInputStream(new File(sourceFileName)), new CRC32());
            zis = new ZipInputStream(cis);
            zis.getNextEntry();
            zipDecompressFile(zis, new File(destFileName));
        } finally {
            if (zis != null)
                zis.close();
            if (cis != null)
                cis.close();
        }
    }

    /**
     * zip文件下载
     *
     * @param response：浏览器响应下载流
     * @param fileName:         下载zip压缩包文件名
     * @param zipFile：          zip文件全名
     * @return
     */
    public static void downLoadZip(HttpServletResponse response, String fileName, String zipFile) {
        try {
            File file = new File(zipFile);
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("ISO8859-1"), "UTF-8"));
            response.setContentLength((int) file.length());
            response.setContentType("application/zip");// 定义输出类型

            BufferedInputStream buff = new BufferedInputStream(new FileInputStream(file));
            OutputStream myout = response.getOutputStream();// 从response对象中得到输出流,准备下载
            //拷贝文件
            IOUtils.copy(buff, myout);
            buff.close();
            myout.flush();
            myout.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载文件
     *
     * @param response
     * @param realPath：zip文件路径，循环读取该路径下的文件，生成压缩包
     * @param zipfileName：zip文件名
     * @throws Exception
     */
    public static void downloadZip(HttpServletResponse response, String realPath, String zipfileName) throws Exception {
        //1、生成zip文件
        String zipFileName = realPath + zipfileName;
        ZipHelper.zipCompress(realPath, zipFileName);
        //2 下载
        ZipHelper.downLoadZip(response, zipfileName, zipFileName);
    }

    /**
     * 下载文件
     *
     * @param response
     * @param realPath：zip文件路径，循环读取该路径下的文件，生成压缩包
     * @throws Exception
     */
    public static void downloadZip(HttpServletResponse response, String realPath) throws Exception {
        //1、生成zip文件
        String filename = DateUtils.format(new Date(), "yyyyMMddHHmmss") + ".zip";
        String zipFileName = realPath + filename;
        ZipHelper.zipCompress(realPath, zipFileName);
        //2 下载
        ZipHelper.downLoadZip(response, filename, zipFileName);
    }

    /**
     * 文件读取缓冲区大小
     */
    private static final int CACHE_SIZE = 2048;

    /**
     * 获取当前路径
     *
     * @return
     */
    public static String getCurrentDir() {
        return ZipHelper.class.getClassLoader().getResource(".").getPath();
    }

    /**
     * <p>
     * 文件转换为byte数组
     * </p>
     *
     * @param filePath 文件路径
     * @return
     * @throws IOException
     */
    public static byte[] fileToBytes(String filePath) throws IOException {
        byte[] data = new byte[0];
        File file = null;
        FileInputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            file = new File(filePath);
            if (file.exists() && file.isFile()) {
                in = new FileInputStream(file);
                out = new ByteArrayOutputStream((int) file.length());
                byte[] cache = new byte[CACHE_SIZE];
                int nRead = 0;
                while ((nRead = in.read(cache)) != -1) {
                    out.write(cache, 0, nRead);
                    out.flush();
                }
                data = out.toByteArray();
            }
            return data;
        } finally {
            if (in != null)
                in.close();
            if (out != null)
                out.close();
        }
    }

    /**
     * <p>
     * 二进制数据写文件
     * </p>
     *
     * @param bytes    二进制数据
     * @param filePath 文件生成目录
     * @throws IOException
     */
    public static void bytesToFile(byte[] bytes, String filePath) throws IOException {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new ByteArrayInputStream(bytes);
            File destFile = new File(filePath);
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            destFile.createNewFile();
            out = new FileOutputStream(destFile);
            byte[] cache = new byte[CACHE_SIZE];
            int nRead = 0;
            while ((nRead = in.read(cache)) != -1) {
                out.write(cache, 0, nRead);
                out.flush();
            }
        } finally {
            if (in != null)
                in.close();
            if (out != null)
                out.close();
        }
    }

    /**
     * 确保文件夹存在。不存在则创建
     *
     * @param folder
     */
    public static void ensureFolderExists(File folder) {
        File parentFile = folder.getParentFile();
        //递归查找上级目录，没有则创建
        if (!parentFile.exists()) {
            ensureFolderExists(parentFile);
            parentFile.mkdir();
        }
    }


    /**
     * 删除目录
     *
     * @param dir
     * @return
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (String s : children) {
                boolean success = deleteDir(new File(dir, s));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }

    /**
     * 判断是否位zip文件
     *
     * @param filePath
     * @return
     */
    public static boolean isNotZipFile(String filePath) {
        boolean flag = true;
        if (filePath.substring(filePath.lastIndexOf(".")).equalsIgnoreCase(".zip")) {
            flag = false;
        }
        return flag;
    }

    /**
     * 解压缩
     *
     * @param zipFile  要解压的文件
     * @param destPath 解压到某文件夹
     * @return
     */
    public static List<String> unZip(String zipFile, String destPath) {
        if (isNotZipFile(zipFile)) {
            return null;
        }
        List<String> allFileName = new ArrayList<String>();
        try {
            // 先指定压缩档的位置和档名，建立FileInputStream对象
            FileInputStream fins = new FileInputStream(zipFile);
            // 将fins传入ZipInputStream中
            ZipInputStream zins = new ZipInputStream(fins, Charset.forName("gbk"));
            ZipEntry ze = null;
            byte[] ch = new byte[256];
            while ((ze = zins.getNextEntry()) != null) {
                String fileName = ze.getName();
                fileName = fileName.replace("\\", "/");
                fileName = fileName.substring(fileName.indexOf("/") + 1);

                File zfile = new File(destPath + File.separator + fileName);
                File fpath = new File(zfile.getParentFile().getPath());
                if (ze.isDirectory() || fileName.endsWith("/")) {
                    if (!zfile.exists()) {
                        zfile.mkdirs();
                    }
                    zins.closeEntry();
                } else {
                    if (!fpath.exists()) {
                        fpath.mkdirs();
                    }

                    //判断文件是否是目录
                    if (zfile.isDirectory() && !zfile.exists()) {
                        zfile.mkdirs();
                    } else {
                        FileOutputStream fouts = new FileOutputStream(zfile);
                        int i;
                        allFileName.add(zfile.getAbsolutePath());
                        while ((i = zins.read(ch)) != -1) {
                            fouts.write(ch, 0, i);
                        }
                        zins.closeEntry();
                        fouts.close();
                    }
                }
            }
            fins.close();
            zins.close();
        } catch (Exception e) {
            log.error("unZip error 解压缩失败!!:", e);
        }
        return allFileName;
    }


    /**
     * 上传文件保存，返回保存文件路径
     *
     * @param file:上传文件
     * @param destPath：文件保存路径
     */
    public static String upload(MultipartFile file, String destPath) {
        String path = null;
        try {
            path = destPath + file.getOriginalFilename();
            File newFile = new File(path);
            //通过CommonsMultipartFile的方法直接写文件（注意这个时候）
            file.transferTo(newFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return path;
    }

    /**
     * 上传文件(text,word，excel，pdf，zip,tar等等),保存到指定位置，返回保存文件路径
     *
     * @param sourcefile:上传文件
     * @param destPath：文件保存路径
     */
    public static String upload(File sourcefile, File destPath) {
        String path = null;
        try {
            //通过CommonsMultipartFile的方法直接写文件（注意这个时候）
            sourcefile.renameTo(destPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return path;
    }

    /**
     * 解压缩zip文件
     *
     * @param zip:       zip文件
     * @param path：解压缩目录
     */
    public static void unZipFile(File zip, String path) {
        try {
            //创建目录
            ZipHelper.createFolder(path);
            ZipFile zipFile = new ZipFile(zip);
            Enumeration<? extends ZipEntry> entrie = zipFile.entries();
            while (entrie.hasMoreElements()) {
                ZipEntry entry = entrie.nextElement();
                String fileName = entry.getName();
                String filePath = path + "/" + fileName;

                if (entry.isDirectory()) {
                    ZipHelper.createFolder(filePath);
                } else {
                    File file = new File(filePath);
                    file.createNewFile();

                    InputStream is = zipFile.getInputStream(entry);
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
                    IOUtils.copy(is, bos);
                    bos.close();
                    is.close();
                }
            }
            zipFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取指定路径下所有文件
     *
     * @param path
     * @return
     */
    public static List<String> getListFiles(String path) {
        List<String> files = new ArrayList<>();
        getListFiles(files, path);
        return files;
    }

    /**
     * 获取指定路径下所有文件
     *
     * @param path
     * @return
     */
    public static void getListFiles(List<String> files, String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            File[] files1 = file.listFiles();
            for (File file1 : files1) {
                String path1 = file1.getPath();
                if (file1.isDirectory()) {
                    getListFiles(files, path1);
                } else {
                    files.add(path1);
                }
            }
        } else {
            files.add(file.getAbsoluteFile().getPath());
        }
    }


    /**
     * 创建目录
     *
     * @param path
     */
    private static void createFolder(String path) {
        try {
            File file = new File(path);
            if (!file.exists()) {
                file.mkdirs();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 压缩文件
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            //将work下的文件压缩
            String sourceFile = "D:\\work";
            String outFile = "D:\\work\\yasuo.zip";
            ZipHelper.zipCompress(sourceFile, outFile);

            //下载zip文件
            String realPath = "D:\\work";
            String zipFileName = DateUtils.format(new Date(), "yyyyMMddHHmmss") + ".zip";
//            ZipHelper.downloadZip(response, realPath, zipFileName);

            //或者路径下所有文件
            List<String> listFiles = ZipHelper.getListFiles(sourceFile);
            for (String listFile : listFiles) {
                System.out.println(listFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
